import json
import os
import shutil
import zipfile
import tempfile
import hashlib
import time
from flask import Blueprint, render_template, request, jsonify, current_app
from apps.modules.admin.models.sys.sys_themes import Themes
from apps.extensions import db
from apps.common.utils.rights import authorize
from apps.common.admin import admin_log
from werkzeug.utils import secure_filename
from apps.common.utils import upload as upload_curd
from apps.common.utils import clear_theme_cache
from apps.extensions.ThemeManager import init_theme_manager, get_theme_manager

themes_bp = Blueprint('themes', __name__, url_prefix='/themes')
def fail_api(msg: str = "失败"):
    """
    返回失败的 API 响应。

    :param msg: 失败消息内容，默认为 "失败"。
    :return: 返回 JSON 格式的响应，包含 `success` 和 `msg` 字段。
    """
    return jsonify(success=False, msg=msg)
@themes_bp.get('/')
@authorize("system:themes:main", log=True)
def index():
    """主题管理首页"""
    return render_template('system/themes/main.html')

@themes_bp.get('/list')
@authorize("system:themes:main", log=True)
def get_list():
    """获取主题列表"""
    try:
        page = request.args.get('page', 1, type=int)
        limit = request.args.get('limit', 10, type=int)
        
        # 分页查询
        pagination = Themes.query.paginate(
            page=page, 
            per_page=limit, 
            error_out=False
        )
        
        themes_list = []
        for theme in pagination.items:
            themes_list.append({
                'id': theme.id,
                'name': theme.name,
                'screenshot': theme.screenshot,
                'description': theme.description,
                'author': theme.author,
                'author_and_uri': theme.author_and_uri,
                'version': theme.version,
                'active': theme.active,
                'has_update': theme.has_update,
                'update_uri': theme.update_uri,
                'create_time': theme.create_time.strftime('%Y-%m-%d %H:%M:%S') if theme.create_time else '',
                'update_time': theme.update_time.strftime('%Y-%m-%d %H:%M:%S') if theme.update_time else ''
            })
        
        return jsonify({
            'code': 0,
            'msg': '获取成功',
            'count': pagination.total,
            'data': themes_list
        })
    except Exception as e:
        return jsonify({
            'code': 1,
            'msg': f'获取失败: {str(e)}',
            'count': 0,
            'data': []
        })

@themes_bp.get('/add')
@authorize("system:themes:add", log=True)
def add():
    """新增主题页面"""
    return render_template('system/themes/add.html')

@themes_bp.post('/save')
@authorize("system:themes:add", log=True)
def save():
    """保存主题"""
    try:
        data = request.get_json()
        
        # 创建新主题
        theme = Themes(
            name=data.get('name', ''),
            screenshot=data.get('screenshot', ''),
            description=data.get('description', ''),
            author=data.get('author', ''),
            author_and_uri=data.get('author_and_uri', ''),
            version=data.get('version', ''),
            active=data.get('active', 0),
            has_update=data.get('has_update', 0),
            update_uri=data.get('update_uri', '')
        )
        
        db.session.add(theme)
        db.session.commit()
        
        # 记录详细日志
        admin_log(request, True, f"新增主题: {theme.name}")
        
        return jsonify({
            'success': True,
            'msg': '保存成功'
        })
    except Exception as e:
        db.session.rollback()
        # 记录失败日志
        admin_log(request, False, f"新增主题失败: {str(e)}")
        return jsonify({
            'success': False,
            'msg': f'保存失败: {str(e)}'
        })

@themes_bp.get('/edit/<int:id>')
@authorize("system:themes:edit", log=True)
def edit(id):
    """编辑主题页面"""
    theme = Themes.query.get_or_404(id)
    return render_template('system/themes/edit.html', theme=theme)

@themes_bp.post('/update/<int:id>')
@authorize("system:themes:edit", log=True)
def update(id):
    """更新主题"""
    try:
        theme = Themes.query.get_or_404(id)
        data = request.get_json()
        
        old_name = theme.name
        
        # 更新字段
        theme.name = data.get('name', theme.name)
        theme.screenshot = data.get('screenshot', theme.screenshot)
        theme.description = data.get('description', theme.description)
        theme.author = data.get('author', theme.author)
        theme.author_and_uri = data.get('author_and_uri', theme.author_and_uri)
        theme.version = data.get('version', theme.version)
        theme.active = data.get('active', theme.active)
        theme.has_update = data.get('has_update', theme.has_update)
        theme.update_uri = data.get('update_uri', theme.update_uri)
        
        db.session.commit()
        
        # 记录详细日志
        admin_log(request, True, f"更新主题: {old_name} -> {theme.name}")
        
        return jsonify({
            'success': True,
            'msg': '更新成功'
        })
    except Exception as e:
        db.session.rollback()
        # 记录失败日志
        admin_log(request, False, f"更新主题失败: {str(e)}")
        return jsonify({
            'success': False,
            'msg': f'更新失败: {str(e)}'
        })

@themes_bp.post('/delete/<int:id>')
@authorize("system:themes:delete", log=True)
def delete(id):
    """删除主题"""
    try:
        theme = Themes.query.get_or_404(id)
        theme_name = theme.name
        
        # 检查是否为启用的主题
        if theme.active == 1:
            return jsonify({
                'success': False,
                'msg': f'无法删除主题 "{theme_name}"：启用的主题不能删除，请先禁用该主题或启用其他主题'
            })
        
        db.session.delete(theme)
        db.session.commit()
        
        # 记录详细日志
        admin_log(request, True, f"删除主题: {theme_name}")
        
        return jsonify({
            'success': True,
            'msg': f'主题 "{theme_name}" 删除成功'
        })
    except Exception as e:
        db.session.rollback()
        # 记录失败日志
        admin_log(request, False, f"删除主题失败: {str(e)}")
        return jsonify({
            'success': False,
            'msg': f'删除失败: {str(e)}'
        })

@themes_bp.post('/batch_delete')
@authorize("system:themes:delete", log=True)
def batch_delete():
    """批量删除主题"""
    try:
        data = request.get_json()
        ids = data.get('ids', [])
        
        if not ids:
            return jsonify({
                'success': False,
                'msg': '请选择要删除的主题'
            })
        
        # 检查是否包含启用的主题
        active_count = Themes.query.filter(
            Themes.id.in_(ids),
            Themes.active == 1
        ).count()
        
        if active_count > 0:
            # 获取启用的主题名称用于错误提示
            active_themes = Themes.query.filter(
                Themes.id.in_(ids),
                Themes.active == 1
            ).with_entities(Themes.name).all()
            active_names = [theme.name for theme in active_themes]
            return jsonify({
                'success': False,
                'msg': f'无法删除启用的主题：{", ".join(active_names)}。请先禁用这些主题或启用其他主题'
            })
        
        # 获取要删除的主题名称（在删除前获取）
        themes_to_delete = Themes.query.filter(Themes.id.in_(ids)).with_entities(Themes.name).all()
        theme_names = [theme.name for theme in themes_to_delete]
        
        # 批量删除
        delete_count = Themes.query.filter(Themes.id.in_(ids)).delete(synchronize_session=False)
        db.session.commit()
        
        # 记录详细日志
        admin_log(request, True, f"批量删除主题: {', '.join(theme_names)}")
        
        return jsonify({
            'success': True,
            'msg': f'成功删除 {delete_count} 个主题：{", ".join(theme_names)}'
        })
    except Exception as e:
        db.session.rollback()
        # 记录失败日志
        admin_log(request, False, f"批量删除主题失败: {str(e)}")
        return jsonify({
            'success': False,
            'msg': f'批量删除失败: {str(e)}'
        })

@themes_bp.post('/toggle_active/<int:id>')
@authorize("system:themes:edit", log=True)
def toggle_active(id):
    theme_manager = get_theme_manager()
    """切换主题启用状态"""
    try:
        theme = Themes.query.get_or_404(id)
        old_status = "启用" if theme.active == 1 else "禁用"
        
        # 如果要启用主题
        if theme.active == 0:
            # 先禁用所有其他主题
            Themes.query.filter(Themes.id != id).update({'active': 0})
            # 然后启用当前主题
            theme.active = 1
            new_status = "启用"
            theme_manager.enable_theme(theme.name)
            
            # 记录详细日志
            admin_log(request, True, f"启用主题: {theme.name}，已禁用其他所有主题")
            
        else:
            # 如果要禁用主题，检查是否还有其他启用的主题
            active_count = Themes.query.filter(Themes.active == 1).count()
            if active_count <= 1:
                return jsonify({
                    'success': False,
                    'msg': '无法禁用主题：必须至少有一个主题处于启用状态'
                })
            
            # 禁用当前主题
            theme.active = 0
            new_status = "禁用"
            
            # 记录详细日志
            admin_log(request, True, f"禁用主题: {theme.name}")
        
        db.session.commit()
        
        # 清除主题缓存
        clear_theme_cache()
        
        return jsonify({
            'success': True,
            'msg': f'主题状态更新成功：{theme.name} {old_status} -> {new_status}',
            'active': theme.active
        })
    except Exception as e:
        db.session.rollback()
        # 记录失败日志
        admin_log(request, False, f"切换主题状态失败: {str(e)}")
        return jsonify({
            'success': False,
            'msg': f'状态更新失败: {str(e)}'
        })

@themes_bp.post('/activate/<int:id>')
@authorize("system:themes:edit", log=True)
def activate_theme(id):
    """启用指定主题（自动禁用其他主题）"""
    try:
        theme = Themes.query.get_or_404(id)
        
        if theme.active == 1:
            return jsonify({
                'success': False,
                'msg': f'主题 "{theme.name}" 已经是启用状态'
            })
        
        # 先禁用所有其他主题
        Themes.query.filter(Themes.id != id).update({'active': 0})
        
        # 启用当前主题
        theme.active = 1
        db.session.commit()
        
        # 清除主题缓存
        clear_theme_cache()
        
        # 记录详细日志
        admin_log(request, True, f"启用主题: {theme.name}，已禁用其他所有主题")
        
        return jsonify({
            'success': True,
            'msg': f'主题 "{theme.name}" 启用成功，其他主题已自动禁用',
            'active': theme.active
        })
    except Exception as e:
        db.session.rollback()
        # 记录失败日志
        admin_log(request, False, f"启用主题失败: {str(e)}")
        return jsonify({
            'success': False,
            'msg': f'启用主题失败: {str(e)}'
        })

@themes_bp.post('/deactivate/<int:id>')
@authorize("system:themes:edit", log=True)
def deactivate_theme(id):
    """禁用指定主题（检查是否至少有一个主题启用）"""
    try:
        theme = Themes.query.get_or_404(id)
        
        if theme.active == 0:
            return jsonify({
                'success': False,
                'msg': f'主题 "{theme.name}" 已经是禁用状态'
            })
        
        # 检查是否还有其他启用的主题
        active_count = Themes.query.filter(Themes.active == 1).count()
        if active_count <= 1:
            return jsonify({
                'success': False,
                'msg': '无法禁用主题：必须至少有一个主题处于启用状态'
            })
        
        # 禁用当前主题
        theme.active = 0
        db.session.commit()
        
        # 清除主题缓存
        clear_theme_cache()
        
        # 记录详细日志
        admin_log(request, True, f"禁用主题: {theme.name}")
        
        return jsonify({
            'success': True,
            'msg': f'主题 "{theme.name}" 禁用成功',
            'active': theme.active
        })
    except Exception as e:
        db.session.rollback()
        # 记录失败日志
        admin_log(request, False, f"禁用主题失败: {str(e)}")
        return jsonify({
            'success': False,
            'msg': f'禁用主题失败: {str(e)}'
        })


@themes_bp.post('/upload')
@authorize("system:themes:edit", log=True)
def upload_api():
    """
    上传文件 - 支持图片和视频
    """
    # 检查是否有文件上传
    if not request.files:
        return fail_api(msg="没有选择文件")

    # 根据uploadFormName获取文件
    file = None
    file_type = None

    if 'image' in request.files:
        file = request.files['image']
        file_type = 'images'
    elif 'video' in request.files:
        file = request.files['video']
        file_type = 'video'
    elif 'attachment' in request.files:
        file = request.files['attachment']
        file_type = 'attachment'
    else:
        return fail_api(msg="未找到有效的文件")

    # 检查是否有meta参数指定文件类型
    if request.form.get('meta'):
        try:
            meta_data = json.loads(request.form.get('meta'))
            if meta_data.get('type'):
                file_type = meta_data['type']
        except:
            pass  # 如果meta解析失败，使用默认的文件类型检测

    if not file or not file.filename:
        return fail_api(msg="文件无效")

    mime = file.content_type

    try:
        # 检查文件是否允许上传
        if not upload_curd.is_allowed_file(file.filename, file_type):
            return fail_api(msg=f"不支持的文件类型: {file.filename}")

        # 上传文件
        result = upload_curd.upload_file_by_type(file=file, mime=mime, file_type=file_type)

        # 根据文件类型返回不同的响应格式
        if file_type == 'images':
            # 图片响应格式
            res = {
                "errorCode": 0,
                "data": {
                    "src": "/admin" + result['url'],
                    "alt": result['filename']
                }
            }
        elif file_type == 'video':
            # 视频响应格式
            res = {
                "errorCode": 0,
                "data": {
                    "src": "/admin" + result['url'],
                    "poster": "",  # 视频封面，可选
                    "alt": result['filename']
                }
            }
        elif file_type == 'attachment':
            # 附件响应格式
            res = {
                "errorCode": 0,
                "data": {
                    "href": "/admin" + result['url'],
                    "fileName": result['filename']
                }
            }
        return jsonify(res)

    except Exception as e:
        return fail_api(msg=f"上传失败: {str(e)}")

@themes_bp.post('/upload_theme')
@authorize("system:themes:add", log=True)
def upload_theme():
    """上传主题ZIP文件"""
    try:
        # 检查是否有文件上传
        if 'file' not in request.files:
            return jsonify({
                'success': False,
                'msg': '没有选择文件'
            })
        
        file = request.files['file']
        if file.filename == '':
            return jsonify({
                'success': False,
                'msg': '没有选择文件'
            })
        
        # 检查文件格式
        if not file.filename.lower().endswith('.zip'):
            return jsonify({
                'success': False,
                'msg': '只支持ZIP格式的文件'
            })
        
        # 创建临时目录
        with tempfile.TemporaryDirectory() as temp_dir:
            # 保存上传的文件
            filename = secure_filename(file.filename)
            zip_path = os.path.join(temp_dir, filename)
            file.save(zip_path)
            
            # 验证ZIP文件
            try:
                with zipfile.ZipFile(zip_path, 'r') as zip_ref:
                    # 检查ZIP文件是否包含必要的文件
                    file_list = zip_ref.namelist()
                    
                    # 检查是否包含theme.json文件
                    theme_json_files = [f for f in file_list if f.endswith('theme.json')]
                    if not theme_json_files:
                        return jsonify({
                            'success': False,
                            'msg': 'ZIP文件必须包含theme.json文件'
                        })
                    
                    # 检查是否包含静态文件
                    has_static = any('static' in f for f in file_list)
                    if not has_static:
                        return jsonify({
                            'success': False,
                            'msg': 'ZIP文件必须包含static目录'
                        })
                    
                    # 解压到临时目录
                    zip_ref.extractall(temp_dir)
                    
                    # 读取theme.json文件
                    theme_json_path = None
                    for theme_json_file in theme_json_files:
                        full_path = os.path.join(temp_dir, theme_json_file)
                        if os.path.exists(full_path):
                            theme_json_path = full_path
                            break
                    
                    if not theme_json_path:
                        return jsonify({
                            'success': False,
                            'msg': '无法找到theme.json文件'
                        })
                    
                    # 读取theme.json内容
                    try:
                        with open(theme_json_path, 'r', encoding='utf-8') as f:
                            theme_config = json.load(f)
                    except json.JSONDecodeError:
                        return jsonify({
                            'success': False,
                            'msg': 'theme.json文件格式错误'
                        })
                    except Exception as e:
                        return jsonify({
                            'success': False,
                            'msg': f'读取theme.json文件失败: {str(e)}'
                        })
                    
                    # 从theme.json中获取主题信息
                    name = theme_config.get('name', '').strip()
                    description = theme_config.get('description', '').strip()
                    author = theme_config.get('author', '').strip()
                    version = theme_config.get('version', '1.0.0').strip()
                    
                    if not name:
                        return jsonify({
                            'success': False,
                            'msg': 'theme.json中主题名称不能为空'
                        })
                    
                    # 验证主题名称格式：只能为英文、数字和下划线组合，且不能以下划线开头和结尾
                    import re
                    theme_name_pattern = re.compile(r'^[a-zA-Z][a-zA-Z0-9_]*[a-zA-Z0-9]$')
                    if not theme_name_pattern.match(name):
                        return jsonify({
                            'success': False,
                            'msg': '主题名称格式错误：只能使用英文、数字和下划线，且不能以下划线开头和结尾'
                        })
                    
                    if not author:
                        return jsonify({
                            'success': False,
                            'msg': 'theme.json中作者名称不能为空'
                        })
                    
                    # 检查主题名称是否已存在
                    existing_theme = Themes.query.filter_by(name=name).first()
                    if existing_theme:
                        return jsonify({
                            'success': False,
                            'msg': f'主题名称 "{name}" 已存在'
                        })
                    
                    # 解压到目标目录
                    target_dir = os.path.join(current_app.root_path, 'modules', 'main', 'templates')
                    theme_dir = os.path.join(target_dir, name)
                    
                    # 如果目录已存在，先删除
                    if os.path.exists(theme_dir):
                        shutil.rmtree(theme_dir)
                    
                    # 解压文件
                    zip_ref.extractall(target_dir)
                    
                    # 重命名解压后的目录（如果ZIP根目录不是主题名称）
                    extracted_items = [item for item in os.listdir(target_dir) 
                                    if os.path.isdir(os.path.join(target_dir, item)) and 
                                    item not in ['admin', 'system', 'cms']]

                    
                    # 处理screenshot.png文件
                    screenshot_url = ''
                    screenshot_path = os.path.join(theme_dir, 'screenshot.png')
                    
                    if os.path.exists(screenshot_path):
                        try:
                            # 使用upload_curd上传screenshot.png文件
                            from werkzeug.datastructures import FileStorage
                            
                            # 创建FileStorage对象
                            with open(screenshot_path, 'rb') as f:
                                file_data = f.read()
                            
                            # 创建FileStorage对象
                            file_storage = FileStorage(
                                stream=open(screenshot_path, 'rb'),
                                filename='screenshot.png',
                                content_type='image/png'
                            )
                            
                            # 使用upload_curd上传文件
                            result = upload_curd.upload_file_by_type(
                                file=file_storage, 
                                mime='image/png', 
                                file_type='images'
                            )
                            
                            if result.get('url'):
                                screenshot_url = "/admin" + result.get('url')
                            else:
                                print(f"上传screenshot.png失败: {result.get('msg', '未知错误')}")
                            
                        except Exception as e:
                            # 如果处理screenshot失败，记录错误但不影响主题上传
                            print(f"处理screenshot.png失败: {str(e)}")
                            screenshot_url = ''
            
            except zipfile.BadZipFile:
                return jsonify({
                    'success': False,
                    'msg': 'ZIP文件损坏或格式不正确'
                })
            
            # 保存主题信息到数据库
            theme = Themes(
                name=name,
                screenshot=screenshot_url,  # 使用处理后的screenshot URL
                description=description,
                author=author,
                author_and_uri='',
                version=version,
                active=0,  # 默认禁用，需要手动启用
                has_update=0,
                update_uri=''
            )
            
            db.session.add(theme)
            db.session.commit()
            
            # 记录详细日志
            admin_log(request, True, f"上传主题: {name} (作者: {author}, 版本: {version})")
            
            return jsonify({
                'success': True,
                'msg': f'主题 "{name}" 上传成功'
            })
    
    except Exception as e:
        db.session.rollback()
        # 记录失败日志
        admin_log(request, False, f"上传主题失败: {str(e)}")
        return jsonify({
            'success': False,
            'msg': f'上传失败: {str(e)}'
        }) 